#ifndef XG_WINX_GUI_CPP
#define XG_WINX_GUI_CPP
////////////////////////////////////////////////////////
#include "../gui.h"

#ifndef XG_FILENAME_LEN
#define XG_FILENAME_LEN	4096
#endif

static wchar_t __buffer__[1024 * 1024];

string winx::WStringToString(const wstring& str)
{
	if (str.empty()) return "";

	SmartBuffer buffer(str.length() * sizeof(wchar_t) + sizeof(wchar_t));

	if (WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1, buffer.str(), buffer.size(), NULL, NULL) <= 0) return "";

	return stdx::utfcode(buffer.str());
}
wstring winx::StringToWString(const string& str)
{
	if (str.empty()) return L"";

	string tmp = stdx::syscode(str);
	SmartBuffer buffer(tmp.length() * sizeof(wchar_t) + sizeof(wchar_t));

	if (MultiByteToWideChar(CP_ACP, 0, tmp.c_str(), -1, (wchar_t*)(buffer.str()), buffer.size()) <= 0) return L"";

	return (wchar_t*)(buffer.str());
}

HWND winx::GetTrayWnd()
{
	return FindWindowW(L"SHELL_TRAYWND", NULL);
}
bool winx::IsKeyDown(int key)
{
	return (GetAsyncKeyState(key) & 0x8000) == 0x8000;
}
bool winx::GetScreenSize(SIZE& sz)
{
	sz.cx = GetSystemMetrics(SM_CXSCREEN);
	sz.cy = GetSystemMetrics(SM_CYSCREEN);

	return sz.cx > 0 && sz.cy > 0;
}
bool winx::GetScrollSize(SIZE& sz)
{
	sz.cx = GetSystemMetrics(SM_CXVSCROLL);
	sz.cy = GetSystemMetrics(SM_CYHSCROLL);

	return sz.cx > 0 && sz.cy > 0;
}
bool winx::GetScreenClientBounds(RECT& rect)
{
	SIZE sz;
	HWND hwnd = winx::GetTrayWnd();

	CHECK_FALSE_RETURN(winx::GetScreenSize(sz) && GetWindowRect(hwnd, &rect));

	int cx = rect.right - rect.left;
	int cy = rect.bottom - rect.top;

	if (cx > cy)
	{
		rect.left = 0;

		if (rect.top < 8)
		{
			rect.top = rect.bottom;
			rect.bottom = sz.cy;
		}
		else
		{
			rect.top = 0;
			rect.bottom = sz.cy - cy;
		}
	}
	else
	{
		rect.top = 0;

		if (rect.left < 8)
		{
			rect.left = rect.right;
			rect.right = sz.cx;
		}
		else
		{
			rect.left = 0;
			rect.right = sz.cx - cx;
		}
	}

	return true;
}
void winx::SetAlpha(HWND hwnd, float alpha)
{
	HINSTANCE handle = LoadLibraryW(L"USER32.DLL");

	if (handle)
	{
		typedef BOOL(WINAPI* FUNC)(HWND, DWORD, BYTE, DWORD);
		FUNC func = (FUNC)GetProcAddress(handle, "SetLayeredWindowAttributes");

		if (func) func(hwnd, 0, (unsigned char)(alpha * 255), 2);

		FreeLibrary(handle);
	}
}
void winx::SetRgn(HWND hwnd, HRGN hrgn)
{
	if (hwnd && hrgn) SetWindowRgn(hwnd, hrgn, true);
}
bool winx::GetDesktopPath(LPWSTR path)
{
	LPITEMIDLIST ppidl = NULL;

	CHECK_FALSE_RETURN(SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &ppidl) == S_OK);
	CHECK_FALSE_RETURN(SHGetPathFromIDListW(ppidl, path));

	CoTaskMemFree(ppidl);

	return true;
}
bool winx::GetQuickLaunchPath(LPWSTR path)
{
	LPITEMIDLIST ppidl;

	CHECK_FALSE_RETURN(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &ppidl) == S_OK);
	CHECK_FALSE_RETURN(SHGetPathFromIDListW(ppidl, path));

	wcscat(path, L"\\Microsoft\\Internet Explorer\\Quick Launch");
	CoTaskMemFree(ppidl);

	return true;
}
void winx::SetWindowIcon(HWND hwnd, HICON hicon)
{
	SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)(hicon));
	SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)(hicon));
}
bool winx::GetProgramsPath(LPWSTR path)
{
	LPITEMIDLIST ppidl;

	CHECK_FALSE_RETURN(SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &ppidl) == S_OK);
	CHECK_FALSE_RETURN(SHGetPathFromIDListW(ppidl, path));
	
	CoTaskMemFree(ppidl);

	return true;
}
bool winx::CenterWindow(HWND hwnd, int cx, int cy, HWND parent)
{
	RECT rect;

	if (parent == NULL)
	{
		CHECK_FALSE_RETURN(GetScreenClientBounds(rect));
	}
	else
	{
		CHECK_FALSE_RETURN(GetClientRect(parent, &rect));
	}

	int x = (rect.right - rect.left - cx) / 2;
	int y = (rect.bottom - rect.top - cy) / 2;

	CHECK_FALSE_RETURN(MoveWindow(hwnd, x, y, cx, cy, TRUE));

	return true;
}
bool winx::ShowMessage(LPCWSTR text, LPCWSTR title, bool confirm, bool topmost)
{
	if (title && *title)
	{
		std::thread([=](){
			HWND hwnd = NULL;

			for (int i = 0; i < 100; i++)
			{
				Sleep(10);

				if (hwnd = FindWindowW(NULL, title))
				{
					winx::SetWindowIcon(hwnd, LoadIcon(NULL, confirm ? IDI_QUESTION : IDI_INFORMATION));

					break;
				}
			}
		}).detach();
	}

	int flag = confirm ? MB_ICONQUESTION | MB_OKCANCEL : MB_ICONINFORMATION | MB_OK;

	return MessageBoxW(NULL, text, title, topmost ? MB_SYSTEMMODAL | flag : flag) == IDOK;
}
bool winx::CreateShortcut(LPCWSTR exepath, LPCWSTR linkname, LPCWSTR linkpath, WORD hotkey, LPCWSTR description, int showcmd)
{
	CHECK_FALSE_RETURN(exepath);

	if (linkpath == NULL)
	{
		CHECK_FALSE_RETURN(GetDesktopPath(__buffer__));

		linkpath = __buffer__;
	}

	HRESULT hr;
	IShellLinkW* link;
	IPersistFile* ppf;

	hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (void**)(&link));

	if (FAILED(hr))
	{
		CoInitialize(NULL);

		hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (void**)(&link));
		
		if (FAILED(hr)) return false;
	}

	hr = link->QueryInterface(IID_IPersistFile, (void**)(&ppf));
	
	if (FAILED(hr))
	{
		link->Release();

		return false;
	}

	link->SetPath(exepath);

	if (hotkey) link->SetHotkey(hotkey);

	if (description) link->SetDescription(description);

	link->SetShowCmd(showcmd);

	WCHAR szBuffer[MAX_PATH];

	wcscpy(szBuffer, linkpath);
	wcscat(szBuffer, L"\\");

	if (linkname)
	{
		wcscat(szBuffer, linkname);
		wcscat(szBuffer, L".lnk");
	}
	else
	{
		linkname = wcsrchr(exepath, L'\\');

		if (linkname == NULL)
		{
			linkname = wcsrchr(exepath, L'/');

			if (linkname == NULL)
			{
				ppf->Release();
				link->Release();

				return false;
			}
		}

		++linkname;

		wcscat(szBuffer, linkname);

		int len = wcslen(szBuffer);

		szBuffer[len - 3] = L'l';
		szBuffer[len - 2] = L'n';
		szBuffer[len - 1] = L'k';
		szBuffer[len] = 0;
	}

	hr = ppf->Save(szBuffer, TRUE);

	ppf->Release();
	link->Release();

	return SUCCEEDED(hr);
}

FileChooser::FileChooser(HWND hwnd, bool multisel, LPCWSTR filter)
{
	init(hwnd, multisel, filter);
}

void FileChooser::init(HWND hwnd, bool multisel, LPCWSTR filter)
{
	DWORD flag = OFN_EXPLORER;

	if (multisel) flag |= OFN_ALLOWMULTISELECT;

	ZeroMemory(&ofn, sizeof(ofn));

	ofn.Flags = flag;
	ofn.hwndOwner = hwnd;
	ofn.nFilterIndex = 1;
	ofn.lpstrFilter = filter;
	ofn.lStructSize = sizeof(ofn);
	ofn.hInstance = NULL;
	ofn.lpstrFile = __buffer__;
	ofn.nMaxFile = XG_FILENAME_LEN;

	__buffer__[0] = 0;
}

void FileChooser::setFilter(LPCWSTR filter)
{
	ofn.lpstrFilter = filter;
}

void FileChooser::setFilterIndex(int index)
{
	ofn.nFilterIndex = index + 1;
}

int FileChooser::getFilterIndex()
{
	return ofn.nFilterIndex - 1;
}

void FileChooser::setParent(HWND hwnd)
{
	ofn.hwndOwner = hwnd;
}

void FileChooser::setFlag(DWORD flag)
{
	ofn.Flags = flag;
}

void FileChooser::setTitle(LPCWSTR title)
{
	ofn.lpstrTitle = title;
}

void FileChooser::setFile(LPCWSTR path)
{
	if (path)
	{
		size_t len = wcslen(path) + 1;

		if (len < XG_FILENAME_LEN)
		{
			memcpy(__buffer__, path, len * sizeof(WCHAR));
		}
	}
}

LPWSTR FileChooser::getFile(bool saved)
{
	if (saved)
	{
		if (GetSaveFileNameW(&ofn)) return __buffer__;
	}
	else
	{
		if (GetOpenFileNameW(&ofn)) return __buffer__;
	}

	return NULL;
}


FolderChooser::FolderChooser(HWND hwnd)
{
	init(hwnd);
}

void FolderChooser::init(HWND hwnd)
{
	ZeroMemory(&browseinfo, sizeof(BROWSEINFO));

	browseinfo.hwndOwner = hwnd;
	browseinfo.ulFlags = BIF_RETURNONLYFSDIRS;
	browseinfo.pszDisplayName = __buffer__;
}

void FolderChooser::setParent(HWND hwnd)
{
	browseinfo.hwndOwner = hwnd;
}

void FolderChooser::setTitle(LPCWSTR title)
{
	browseinfo.lpszTitle = title;
}

void FolderChooser::setFlag(DWORD flag)
{
	browseinfo.ulFlags = flag;
}

LPWSTR FolderChooser::getFolder()
{
	std::thread([=](){
			HWND hwnd = NULL;

			for (int i = 0; i < 100; i++)
			{
				Sleep(10);

				if (hwnd = FindWindowW(NULL, L"浏览文件夹"))
				{
					winx::SetWindowIcon(hwnd, LoadIcon(NULL, IDI_INFORMATION));

					break;
				}
			}
		}).detach();

	LPITEMIDLIST idlist = ::SHBrowseForFolderW(&browseinfo);

	if (idlist && SHGetPathFromIDListW(idlist, __buffer__)) return __buffer__;

	return NULL;
}

FileFinder::FileFinder()
{
	findata.cFileName[0] = 0;
	handle = INVALID_HANDLE_VALUE;
}

FileFinder::~FileFinder()
{
	close();
}

void FileFinder::close()
{
	if (handle == INVALID_HANDLE_VALUE)
	{
		FindClose(handle);
		handle = INVALID_HANDLE_VALUE;
	}
}

LPCWSTR FileFinder::getFirstFile(LPCWSTR filter)
{
	close();

	if (filter == NULL) return NULL;

	handle = ::FindFirstFileW(filter, &findata);

	if (handle == INVALID_HANDLE_VALUE)
	{
		findata.cFileName[0] = 0;

		return NULL;
	}

	return findata.cFileName;
}

LPCWSTR FileFinder::getNextFile()
{
	if (handle == INVALID_HANDLE_VALUE)
	{
		findata.cFileName[0] = 0;

		return NULL;
	}

	if (FindNextFileW(handle, &findata)) return findata.cFileName;

	findata.cFileName[0] = 0;

	return NULL;
}

bool FileFinder::isDots()
{
	return findata.cFileName[0] == L'.';
}

bool FileFinder::isFolder()
{
	if (handle == INVALID_HANDLE_VALUE)
	{
		findata.cFileName[0] = 0;

		return false;
	}

	return (FILE_ATTRIBUTE_DIRECTORY & findata.dwFileAttributes) ? true : false;
}

bool FileFinder::isHidden()
{
	if (handle == INVALID_HANDLE_VALUE)
	{
		findata.cFileName[0] = 0;

		return false;
	}

	return (FILE_ATTRIBUTE_HIDDEN & findata.dwFileAttributes) ? true : false;
}

bool FileFinder::isReadOnly()
{
	if (handle == INVALID_HANDLE_VALUE)
	{
		findata.cFileName[0] = 0;

		return false;
	}

	return (FILE_ATTRIBUTE_READONLY & findata.dwFileAttributes) ? true : false;
}

DWORD FileFinder::getAttributes()
{
	if (handle == INVALID_HANDLE_VALUE)
	{
		findata.cFileName[0] = 0;

		return DWORD(XG_ERROR);
	}

	return findata.dwFileAttributes;
}
////////////////////////////////////////////////////////
#endif
